/*!
\copyright  Copyright (c) 2015 - 2017 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\version    Part of 6.3.0
\file       av_headset_peer_signalling.c
\brief	    Implementation of module providing signalling to headset peer device.
*/

#include "av_headset.h"
#include "av_headset_peer_signalling.h"
#include "av_headset_device.h"
#include "av_headset_config.h"
#include "av_headset_scan_manager.h"
#include "av_headset_log.h"

#include <panic.h>
#include <message.h>
#include <bdaddr.h>

/******************************************************************************
 * General Definitions
 ******************************************************************************/

/*! Macro to make a message. */
#define MAKE_MESSAGE(TYPE) TYPE##_T *message = PanicUnlessNew(TYPE##_T);
/*! Macro to make message with variable length for array fields. */
#define MAKE_PEER_SIG_MESSAGE_WITH_LEN(TYPE, LEN) \
    TYPE##_T *message = (TYPE##_T *) PanicUnlessMalloc(sizeof(TYPE##_T) + LEN);

/******************************************************************************
 * Peer Signalling Message Definitions
 ******************************************************************************/
/*
 * AVRCP_PEER_CMD_ADD_LINK_KEY
 */
/*! OPID for sending handset link key to peer headset. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY                     0x34
/*! Size of message to send link key to peer in bytes. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY_SIZE                24
/*! Byte offset to address type field. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_OFFSET    0
/*! Byte offset to address. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_OFFSET         1
/*! Byte offset to key type field. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_TYPE_OFFSET     7
/*! Byte offset to link key field. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_OFFSET          8

/*! BR/EDR address type. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_BREDR     0
/*! BR/EDR link key, generated by key H6/H7 deriviation process. */
#define AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_TYPE_0          0

/*! Link key size of 16-bit words */
#define SIZE_LINK_KEY_16BIT_WORDS   8

/*
 * AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS
 */
/*! OPID for sending pair handset address msg to peer. */
#define AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS                     0x33
/*! Size of AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS message. */
#define AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_SIZE                7
/*! Byte offset to address type field. */
#define AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_ADDR_TYPE_OFFSET    0
/*! Byte offset to address field. */
#define AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_ADDR_OFFSET         1

/*! Definitions for Peer Signalling message channel packet format. */
/*!@{ */
#define AVRCP_PEER_CMD_MSG_CHANNEL_MSG                  0x41
#define AVRCP_PEER_CMD_MSG_CHANNEL_HEADER_SIZE          6
#define AVRCP_PEER_CMD_MSG_CHANNEL_ID_OFFSET            0       /*! Msg Channel IDs are 32 bit */
#define AVRCP_PEER_CMD_MSG_CHANNEL_DATA_LENGTH_OFFSET   4       /*! Length limited to 16-bit by AVRCP API */
#define AVRCP_PEER_CMD_MSG_CHANNEL_DATA_OFFSET          6
/*!@} */

static void appPeerSigMsgConnectionInd(peerSigStatus status);
static void appPeerSigStartInactivityTimer(void);
static void appPeerSigCancelInactivityTimer(void);
static void appPeerSigCancelInProgressOperation(void);



static void appPeerSigEnterConnectingAcl(void)
{
    DEBUG_LOG("appPeerSigEnterConnectingAcl");
}

static void appPeerSigExitConnectingAcl(void)
{
    DEBUG_LOG("appPeerSigExitConnectingAcl");
}

static void appPeerSigEnterConnectingLocal(void)
{
    DEBUG_LOG("appPeerSigEnterConnectingLocal");
}

static void appPeerSigExitConnectingLocal(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOG("appPeerSigExitConnectingLocal");

    /* We have finished (successfully or not) attempting to connect, so
     * we can relinquish our lock on the ACL.  Bluestack will then close
     * the ACL when there are no more L2CAP connections */
    appConManagerReleaseAcl(&peer_sig->peer_addr);
}

static void appPeerSigEnterConnectingRemote(void)
{
    DEBUG_LOG("appPeerSigEnterConnectingRemote");
}

static void appPeerSigExitConnectingRemote(void)
{
    DEBUG_LOG("appPeerSigExitConnectingRemote");
}

static void appPeerSigEnterConnected(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOG("appPeerSigEnterConnected");

    /* Cancel any other startup requests */
    MessageCancelAll(&peer_sig->task, PEER_SIG_INTERNAL_STARTUP_REQ);

    /* If we have any clients inform them of peer signalling connection */
    appPeerSigMsgConnectionInd(peerSigStatusConnected);

    appPeerSigStartInactivityTimer();
}

static void appPeerSigExitConnected(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOG("appPeerSigExitConnected");

    appPeerSigCancelInactivityTimer();

    /* If we have any clients inform them of peer signalling disconnection */
    appPeerSigMsgConnectionInd(peer_sig->link_loss_occurred ? peerSigStatusLinkLoss : peerSigStatusDisconnected);
}

static void appPeerSigEnterDisconnected(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOG("appPeerSigEnterDisconnected");

    /* Cancel any operation in progress */
    appPeerSigCancelInProgressOperation();

    /* Clear peer address, as we use that to detect if we've previously reject a peer connection */
    BdaddrSetZero(&peer_sig->peer_addr);

    appScanManagerEnablePageScan(SCAN_MAN_USER_PEERSIG, SCAN_MAN_PARAMS_TYPE_SLOW);
}

static void appPeerSigExitDisconnected(void)
{
    DEBUG_LOG("appPeerSigExitDisconnected");

    appScanManagerDisablePageScan(SCAN_MAN_USER_PEERSIG);
}

static appPeerSigState appPeerSigGetState(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    return peer_sig->state;
}

static void appPeerSigSetState(appPeerSigState state)
{
    peerSigTaskData *peer_sig = appGetPeerSig();

    appPeerSigState old_state = appPeerSigGetState();
    DEBUG_LOGF("appPeerSigSetState, new state %x, old state %x", state, old_state);

    /* Handle state exit functions */
    switch (old_state)
    {
        case PEER_SIG_STATE_DISCONNECTED:
            appPeerSigExitDisconnected();
            break;
        case PEER_SIG_STATE_CONNECTING_ACL:
            appPeerSigExitConnectingAcl();
            break;
        case PEER_SIG_STATE_CONNECTING_LOCAL:
            appPeerSigExitConnectingLocal();
            break;
        case PEER_SIG_STATE_CONNECTING_REMOTE:
            appPeerSigExitConnectingRemote();
            break;
        case PEER_SIG_STATE_CONNECTED:
            appPeerSigExitConnected();
            break;
        default:
            break;
    }

    /* Set new state */
    peer_sig->state = state;

    /* Update lock according to state */
    if (state & PEER_SIG_STATE_LOCK)
        peer_sig->lock |= 0x01;
    else
        peer_sig->lock &= ~0x01;

    /* Handle state entry functions */
    switch (state)
    {
        case PEER_SIG_STATE_DISCONNECTED:
            appPeerSigEnterDisconnected();
            break;
        case PEER_SIG_STATE_CONNECTING_ACL:
            appPeerSigEnterConnectingAcl();
            break;
        case PEER_SIG_STATE_CONNECTING_LOCAL:
            appPeerSigEnterConnectingLocal();
            break;
        case PEER_SIG_STATE_CONNECTING_REMOTE:
            appPeerSigEnterConnectingRemote();
            break;
        case PEER_SIG_STATE_CONNECTED:
            appPeerSigEnterConnected();
            break;
        default:
            break;
    }
}



static void appPeerSigError(MessageId id)
{
    UNUSED(id);
    DEBUG_LOGF("appPeerSigError, state %u, id %u", appPeerSigGetState(), id);
    Panic();
}


/******************************************************************************
 * Messages sent to API clients
 ******************************************************************************/
/*! \brief Send PEER_SIG_LINK_KEY_TX_CFM message. */
static void appPeerSigMsgLinkKeyConfirmation(Task task, peerSigStatus status, bdaddr* addr)
{
    MAKE_MESSAGE(PEER_SIG_LINK_KEY_TX_CFM);
    message->status = status;
    message->handset_addr = *addr;
    MessageSend(task, PEER_SIG_LINK_KEY_TX_CFM, message);
}

/*! \brief Send PEER_SIG_PAIR_HANDSET_CFM message. */
static void appPeerSigMsgPairHandsetConfirmation(Task task, peerSigStatus status, bdaddr* addr)
{
    MAKE_MESSAGE(PEER_SIG_PAIR_HANDSET_CFM);
    message->status = status;
    message->handset_addr = *addr;
    MessageSend(task, PEER_SIG_PAIR_HANDSET_CFM, message);
}

/*! \brief Send indication of connection state to registered clients. */
static void appPeerSigMsgConnectionInd(peerSigStatus status)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    Task next_client = 0;

    while (appTaskListIterate(peer_sig->peer_sig_client_tasks, &next_client))
    {
        MAKE_MESSAGE(PEER_SIG_CONNECTION_IND);
        message->status = status;
        MessageSend(next_client, PEER_SIG_CONNECTION_IND, message);
    }
}

/*! \brief Send indication of result of message channel transmission to client. */
static void appPeerSigMsgChannelTxConfirmation(Task task, peerSigStatus status,
                                               peerSigMsgChannel channel)
{
    MAKE_MESSAGE(PEER_SIG_MSG_CHANNEL_TX_CFM);
    message->status = status;
    message->channel = channel;
    MessageSend(task, PEER_SIG_MSG_CHANNEL_TX_CFM, message);
}

/******************************************************************************
 * Internal Peer Signalling management functions
 ******************************************************************************/

/*! \brief Cancel any already in progress operations that were waiting for responses from peer.
 */
static void appPeerSigCancelInProgressOperation(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();

    /* Work out what operation is in progress and inform client of failure */
    switch (peer_sig->current_op)
    {
        case AVRCP_PEER_CMD_ADD_LINK_KEY:
            appPeerSigMsgLinkKeyConfirmation(peer_sig->client_task, peerSigStatusLinkKeyTxFail,
                                             &peer_sig->handset_addr);
            break;
        case AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS:
            appPeerSigMsgPairHandsetConfirmation(peer_sig->client_task, peerSigStatusPairHandsetTxFail,
                                                 &peer_sig->handset_addr);
            break;

        case AVRCP_PEER_CMD_MSG_CHANNEL_MSG:
            appPeerSigMsgChannelTxConfirmation(peer_sig->client_task, peerSigStatusMsgChannelTxFail,
                                               peer_sig->current_msg_channel);
            break;

        default:
            break;
    }

    /* Clear up, no operation in progress now */
    peer_sig->client_task = NULL;
    peer_sig->current_op = 0;

    /* Clear lock, this may result in the next message being delivered */
    peer_sig->lock &= ~0x02;
}

/*! \brief Start AVRCP if required.

    Must be called by all peer signalling message request APIs, see #appPeerSigTxLinkKeyToPeerReq().

    @return uint16 Lock with which to conditionally post messages requests against.
*/
static uint16 *appPeerSigStartup(const bdaddr *peer_addr)
{
    peerSigTaskData *peer_sig = appGetPeerSig();

    MAKE_MESSAGE(PEER_SIG_INTERNAL_STARTUP_REQ);
    message->peer_addr = *peer_addr;
    MessageSendConditionally(&peer_sig->task, PEER_SIG_INTERNAL_STARTUP_REQ, message, &peer_sig->lock);
    return &peer_sig->lock;
}

/*! \brief Set the inactivity timer.
 */
static void appPeerSigStartInactivityTimer(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOG("appPeerSigStartInactivityTimer");

    if (appConfigPeerSignallingChannelTimeoutSecs())
    {
        MessageCancelAll(&peer_sig->task, PEER_SIG_INTERNAL_INACTIVITY_TIMER);
        MessageSendLater(&peer_sig->task, PEER_SIG_INTERNAL_INACTIVITY_TIMER, NULL,
                         appConfigPeerSignallingChannelTimeoutSecs() * 1000);
    }
}

/*! \brief Stop the inactivity timer.
 */
static void appPeerSigCancelInactivityTimer(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOG("appPeerSigCancelInactivityTimer");

    MessageCancelAll(&peer_sig->task, PEER_SIG_INTERNAL_INACTIVITY_TIMER);
}

/*! \brief Handle inactivity timer, teardown signalling channel.
 */
static void appPeerSigInactivityTimeout(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOGF("appPeerSigInactivityTimeout, state %u", appPeerSigGetState());

    /* Both earbuds have an inactivity timeout, protect against race where
     * the AVRCP link may have just been disconnected by the other earbud */
    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_CONNECTED:
        {
            /* Start disconnection and move into 'disconnected' state */
            appAvAvrcpDisconnectRequest(&peer_sig->task, peer_sig->av_inst);
            appPeerSigSetState(PEER_SIG_STATE_DISCONNECTED);
        }
        break;

        default:
            break;
    }
}

/******************************************************************************
 * Handlers for connection related messages from AV
 ******************************************************************************/
/*! \brief Take action on connection of AVRCP to peer.
 */
static void appPeerSigHandleAvAvrcpConnectCfm(AV_AVRCP_CONNECT_CFM_T *cfm)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOGF("appPeerSigHandleAvAvrcpConnectCfm, state %u, status %u, av %p",
               peer_sig->state, cfm->status, cfm->av_instance);

    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_CONNECTING_LOCAL:
        case PEER_SIG_STATE_CONNECTING_REMOTE:
        {
            if (cfm->status == avrcp_success)
            {
                /* Save AV instance for AVRCP comms with peer */
                peer_sig->av_inst = cfm->av_instance;

                /* Register peer signalling task with AV to receive vendor unique AVRCP
                   messages */
                appAvrcpVendorPassthroughRegister(peer_sig->av_inst, &peer_sig->task);

                /* Move to 'connected' state */
                appPeerSigSetState(PEER_SIG_STATE_CONNECTED);
            }
            else
            {
                /* Move to 'Disconnected' state */
                appPeerSigSetState(PEER_SIG_STATE_DISCONNECTED);
            }
        }
        break;

        default:
            appPeerSigError(AV_AVRCP_CONNECT_CFM);
            break;
    }
}


/*! \brief Handle indication we now have an AVRCP connection to peer.
 */
static void appPeerSigHandleAvAvrcpConnectInd(const AV_AVRCP_CONNECT_IND_T *ind)
{
    peerSigTaskData * peer_sig = appGetPeerSig();
    DEBUG_LOGF("appPeerSigHandleAvAvrcpConnectInd, state %u", appPeerSigGetState());

    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_DISCONNECTED:
        {
            /* only accept AVRCP connections from paired peer devices. */
            if (appDeviceIsPeer(&ind->bd_addr))
            {
                DEBUG_LOG("appPeerSigHandleAvAvrcpConnectInd, accepted");

                /* Move to 'connecting local' state */
                appPeerSigSetState(PEER_SIG_STATE_CONNECTING_REMOTE);

                /* Accept connection */
                appAvAvrcpConnectResponse(&peer_sig->task, &peer_sig->task, &ind->bd_addr,
                                          ind->connection_id, ind->signal_id, AV_AVRCP_ACCEPT);

                /* Done, so exit */
                return;
            }
            else
            {
                /* Not a known peer, rememeber it just in case we're in the middle of pairing */
                peer_sig->peer_addr = ind->bd_addr;
            }
        }

        default:
        {
            DEBUG_LOG("appPeerSigHandleAvAvrcpConnectInd, rejected");

            /* Reject connection */
            appAvAvrcpConnectResponse(&peer_sig->task, &peer_sig->task, &ind->bd_addr,
                                      ind->connection_id, ind->signal_id, AV_AVRCP_REJECT);
        }
        break;
    }
}

/*! \brief Handle AVRCP to peer disconnection event.
 */
static void appPeerSigHandleAvAvrcpDisconnectInd(AV_AVRCP_DISCONNECT_IND_T *ind)
{
    peerSigTaskData * peer_sig = appGetPeerSig();
    DEBUG_LOGF("appPeerSigHandleAvAvrcpDisconnectInd, status %u, state %u", ind->status, appPeerSigGetState());

    /* Inform clients if link loss and we initiated the original connection */
    if (ind->status == avrcp_link_loss && !BdaddrIsZero(&peer_sig->peer_addr))
    {
        DEBUG_LOG("appPeerSigHandleAvAvrcpDisconnectInd, link-loss");

        /* Set link-loss flag */
        peer_sig->link_loss_occurred = TRUE;
    }
    else
    {
        /* Clear link-loss flag */
        peer_sig->link_loss_occurred = TRUE;
    }

    /* Move to 'disconnected' state */
    appPeerSigSetState(PEER_SIG_STATE_DISCONNECTED);
}


static uint16 appPeerSigReadUint16(const uint8 *data)
{
    return data[0] + ((uint16)data[1] << 8);
}

//static uint24 appPeerSigReadUint24(const uint8 *data)
//{
//    return data[0] + ((uint16)data[1] << 8) + ((uint32)data[2] << 16);
//}

static uint32 appPeerSigReadUint32(const uint8 *data)
{
    return data[0] + (data[1] << 8) + ((uint32)data[2] << 16) + ((uint32)data[3] << 24);
}

static void appPeerSigWriteUint16(uint8 *data, uint16 val)
{
    data[0] = val & 0xFF;
    data[1] = (val >> 8) & 0xFF;
}

//static void appPeerSigWriteUint24(uint8 *data, uint24 val)
//{
//    data[0] = val & 0xFF;
//    data[1] = (val >> 8) & 0xFF;
//    data[2] = (val >> 16) & 0xFF;
//}

static void appPeerSigWriteUint32(uint8 *data, uint32 val)
{
    data[0] = val & 0xFF;
    data[1] = (val >> 8) & 0xFF;
    data[2] = (val >> 16) & 0xFF;
    data[3] = (val >> 24) & 0xFF;
}


static void appPeerSigHandleInternalStartupRequest(PEER_SIG_INTERNAL_STARTUP_REQ_T *req)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOGF("appPeerSigHandleInternalStartupRequest, state %u, bdaddr %04x,%02x,%06lx",
               appPeerSigGetState(),
               req->peer_addr.nap, req->peer_addr.uap, req->peer_addr.lap);

    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_CONNECTING_ACL:
        case PEER_SIG_STATE_DISCONNECTED:
        {
            /* Check if ACL is now up */
            if (appConManagerIsConnected(&req->peer_addr))
            {
                DEBUG_LOG("appPeerSigHandleInternalStartupRequest, ACL connected");

                /* Initiate if we created the ACL, or was previously rejected (peer_addr is not zero) */
                if (appConManagerIsAclLocal(&req->peer_addr) || BdaddrIsSame(&peer_sig->peer_addr, &req->peer_addr))
                {
                    DEBUG_LOG("appPeerSigHandleInternalStartupRequest, ACL locally initiated");

                    /* Store address of peer */
                    peer_sig->peer_addr = req->peer_addr;

                    /* Initate outgoing AVRCP connection */
                    if (appAvAvrcpConnectRequest(&peer_sig->task, &peer_sig->peer_addr))
                        appPeerSigSetState(PEER_SIG_STATE_CONNECTING_LOCAL);
                }
                else
                {
                    DEBUG_LOG("appPeerSigHandleInternalStartupRequest, ACL remotely initiated");

                    /* Not locally initiated ACL, move to 'Disconnected' state */
                    appPeerSigSetState(PEER_SIG_STATE_DISCONNECTED);
                }
            }
            else
            {
                if (appPeerSigGetState() == PEER_SIG_STATE_DISCONNECTED)
                {
                    DEBUG_LOG("appPeerSigHandleInternalStartupRequest, ACL not connected, attempt to open ACL");

                    /* Post message back to ourselves, blocked on creating ACL */
                    MAKE_MESSAGE(PEER_SIG_INTERNAL_STARTUP_REQ);
                    message->peer_addr = req->peer_addr;
                    MessageSendConditionally(&peer_sig->task, PEER_SIG_INTERNAL_STARTUP_REQ, message, appConManagerCreateAcl(&req->peer_addr));

                    /* Wait in 'Connecting ACL' state for ACL to open */
                    appPeerSigSetState(PEER_SIG_STATE_CONNECTING_ACL);
                    return;
                }
                else
                {
                    DEBUG_LOG("appPeerSigHandleInternalStartupRequest, ACL failed to open, giving up");

                    /* ACL failed to open, move to 'Disconnected' state */
                    appPeerSigSetState(PEER_SIG_STATE_DISCONNECTED);
                }
            }
        }
        break;

        case PEER_SIG_STATE_CONNECTED:
            /* Already connected, just ignore startup request */
            break;

        default:
            appPeerSigError(PEER_SIG_INTERNAL_STARTUP_REQ);
            break;
    }

    /* Cancel any other startup requests */
    MessageCancelAll(&peer_sig->task, PEER_SIG_INTERNAL_STARTUP_REQ);
}

static void appPeerSigHandleInternalShutdownReq(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();

    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_CONNECTED:
            if (peer_sig->av_inst)
            {
                appAvAvrcpDisconnectRequest(&peer_sig->task, peer_sig->av_inst);
            }
            appPeerSigSetState(PEER_SIG_STATE_DISCONNECTED);
            break;
        default:
            break;
    }
}

/******************************************************************************
 * Handlers for peer messaging from AV
 ******************************************************************************/
/*! \brief Receive handset link key for peer.
 
    External interface for link keys is 16 bytes packed into 8 16-bit words,
    do the conversion from 16 8-bit words here.
 */
static bool appPeerSigHandleRxLinkKey(AV_AVRCP_VENDOR_PASSTHROUGH_IND_T *ind)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    uint16 key_len_words = SIZE_LINK_KEY_16BIT_WORDS * sizeof(uint16);

    DEBUG_LOGF("appPeerSigHandleRxLinkKey size:%d", ind->size_payload);

    /* validate message:
     * message length not correct OR
     * we don't have a task registered to receive it OR
     * handset address type not supported OR
     * link key type not support */
    if (   (ind->size_payload != AVRCP_PEER_CMD_ADD_LINK_KEY_SIZE)
        || (peer_sig->rx_link_key_task == NULL)
        || (ind->payload[AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_OFFSET] !=
                   AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_BREDR)
        || (ind->payload[AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_TYPE_OFFSET] !=
                    AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_TYPE_0)
        || !peer_sig->rx_link_key_task)
    {
        /* we don't like this message format, indicate failure to sender */
        return FALSE;
    }
    else
    {
        MAKE_PEER_SIG_MESSAGE_WITH_LEN(PEER_SIG_LINK_KEY_RX_IND, key_len_words);
        int index = AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_OFFSET;

        message->status = peerSigStatusSuccess;
        message->handset_addr.lap = (uint32)(((uint32)ind->payload[index]) | 
                                             ((uint32)ind->payload[index+1]) << 8 |
                                             ((uint32)ind->payload[index+2]) << 16);
        message->handset_addr.uap = ind->payload[index+3];
        message->handset_addr.nap = (uint16)(((uint16)ind->payload[index+4]) |
                                             ((uint16)ind->payload[index+5]) << 8);
        index = AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_OFFSET;
        memcpy(message->key, &ind->payload[index], key_len_words);
        message->key_len = SIZE_LINK_KEY_16BIT_WORDS;
    
        /* send to registered client */
        MessageSend(peer_sig->rx_link_key_task, PEER_SIG_LINK_KEY_RX_IND, message);

        /* indicate we succeeded */
        return TRUE;
    }
}

/*! \brief Receive pair handset command. */
static bool appPeerSigHandlePairHandsetCommand(AV_AVRCP_VENDOR_PASSTHROUGH_IND_T *ind)
{
    peerSigTaskData* peer_sig = appGetPeerSig();

    /* validate message */
    if (   (ind->size_payload != AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_SIZE)
        || (ind->payload[AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_ADDR_TYPE_OFFSET] !=
                    AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_BREDR)
        || !peer_sig->rx_pair_handset_task)
    {
        return FALSE;
    }
    else
    {
        /* tell pairing module to pair with specific handset */
        MAKE_MESSAGE(PEER_SIG_PAIR_HANDSET_IND);
        int index = AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_ADDR_OFFSET;
        message->handset_addr.lap = (uint32)(((uint32)ind->payload[index]) | 
                                             ((uint32)ind->payload[index+1]) << 8 |
                                             ((uint32)ind->payload[index+2]) << 16);
        message->handset_addr.uap = ind->payload[index+3];
        message->handset_addr.nap = (uint16)(((uint16)ind->payload[index+4]) |
                                             ((uint16)ind->payload[index+5]) << 8);
        MessageSend(peer_sig->rx_pair_handset_task, PEER_SIG_PAIR_HANDSET_IND, message);
        DEBUG_LOGF("appPeerSigHandlePairHandsetCommand %lx %x %x", message->handset_addr.lap, message->handset_addr.uap, message->handset_addr.nap);
        return TRUE;
    }
}

/*! \brief Handle incoming message channel transmission.
 */
static bool appPeerSigHandleMsgChannelRx(AV_AVRCP_VENDOR_PASSTHROUGH_IND_T *ind)
{
    peerSigTaskData* peer_sig = appGetPeerSig();

    /* validate message 
     *  - must be at least size of a header+1 */
    if (ind->size_payload > AVRCP_PEER_CMD_MSG_CHANNEL_HEADER_SIZE)
    {
        peerSigMsgChannel channel;
        uint16 msg_size;

        channel = appPeerSigReadUint32(&ind->payload[AVRCP_PEER_CMD_MSG_CHANNEL_ID_OFFSET]);
        msg_size = appPeerSigReadUint16(&ind->payload[AVRCP_PEER_CMD_MSG_CHANNEL_DATA_LENGTH_OFFSET]);

        /* make sure the message contains the same amount of data that the length
         * field in the header specifies */
        if (msg_size == (ind->size_payload - AVRCP_PEER_CMD_MSG_CHANNEL_HEADER_SIZE))
        {
            Task task = 0;
            TaskListData data;
            bool handled = FALSE;

            /* look for a task registered to receive messages on this channel and
             * send it the message */
            while (appTaskListIterateWithData(peer_sig->msg_channel_tasks, &task, &data))
            {
                if ((data.u32 & channel) == channel)
                {
                    MAKE_PEER_SIG_MESSAGE_WITH_LEN(PEER_SIG_MSG_CHANNEL_RX_IND, msg_size-1);
                    message->channel = channel;
                    message->msg_size = msg_size;
                    memcpy(message->msg, &ind->payload[AVRCP_PEER_CMD_MSG_CHANNEL_DATA_OFFSET], msg_size);
                    MessageSend(task, PEER_SIG_MSG_CHANNEL_RX_IND, message);
                    handled = TRUE;
                }
            }
            return handled;
        }
    }

    return FALSE;
}

/*! \brief Unsolicited messages received from peer.
 */
static void appPeerSigHandleAvAvrcpVendorPassthroughInd(AV_AVRCP_VENDOR_PASSTHROUGH_IND_T *ind)
{
    bool rc = FALSE;

    switch (ind->opid)
    {
        case AVRCP_PEER_CMD_ADD_LINK_KEY:
            rc = appPeerSigHandleRxLinkKey(ind);
            break;

        case AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS:
            rc = appPeerSigHandlePairHandsetCommand(ind);
            break;

        case AVRCP_PEER_CMD_MSG_CHANNEL_MSG:
            rc = appPeerSigHandleMsgChannelRx(ind);
            break;

        /* add handlers for new incoming peer signalling message types here */

        default:
        break;
    }

    /* Restart in-activity timer */
    appPeerSigStartInactivityTimer();

    /* Reply to the indication */
    appAvrcpVendorPassthroughResponse(ind->av_instance,
                                      rc ? avctp_response_accepted : avctp_response_rejected);
}

/*! \brief Confirmation of messages we've sent to the peer.
 */
static void appPeerSigHandleAvAvrcpVendorPassthroughConfirm(AV_AVRCP_VENDOR_PASSTHROUGH_CFM_T *cfm)
{
    peerSigTaskData* peer_sig = appGetPeerSig();

    DEBUG_LOGF("appPeerSigHandleAvAvrcpVendorPassthroughConfirm %d opid:%x", cfm->status, cfm->opid);

    switch (cfm->opid)
    {
        case AVRCP_PEER_CMD_ADD_LINK_KEY:
            appPeerSigMsgLinkKeyConfirmation(peer_sig->client_task, cfm->status == avrcp_success ?
                                             peerSigStatusSuccess : peerSigStatusLinkKeyTxFail,
                                             &peer_sig->handset_addr);
            break;

        case AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS:
            appPeerSigMsgPairHandsetConfirmation(peer_sig->client_task, cfm->status == avrcp_success ?
                                                 peerSigStatusSuccess : peerSigStatusPairHandsetTxFail,
                                                 &peer_sig->handset_addr);
            break;

        case AVRCP_PEER_CMD_MSG_CHANNEL_MSG:
            appPeerSigMsgChannelTxConfirmation(peer_sig->client_task, cfm->status == avrcp_success ?
                                               peerSigStatusSuccess : peerSigStatusMsgChannelTxFail,
                                               peer_sig->current_msg_channel);
            peer_sig->current_msg_channel = 0;
            break;

        /* add handlers for new outgoing peer signalling message confirmations here */

        default:
            DEBUG_LOGF("appPeerSigHandleAvAvrcpVendorPassthroughConfirm unknown opid:%x", cfm->opid);
            break;
    }

    /* Clear up, no operation in progress now */
    peer_sig->client_task = NULL;
    peer_sig->current_op = 0;

    /* Clear lock, this may result in the next message being delivered */
    peer_sig->lock &= ~0x02;

    /* Completed current operation, restart the inactivity timer */
    if (appPeerSigGetState() == PEER_SIG_STATE_CONNECTED)
        appPeerSigStartInactivityTimer();
}

static void appPeerSigVendorPassthroughRequest(Task client_task,
                                               avc_operation_id op_id,
                                               uint16 size_payload, const uint8 *payload)
{
    peerSigTaskData *peer_sig = appGetPeerSig();

    /* Store task for rsponse and oepration ID, so when confirmation comes back we can send
     * message to correct task */
    peer_sig->client_task = client_task;
    peer_sig->current_op = op_id;

    /* Set lock to prevent any other operations */
    peer_sig->lock |= 0x02;

    /* Request vendor passthrough */
    appAvrcpVendorPassthroughRequest(peer_sig->av_inst, op_id, size_payload, payload);

    /* Cancel inactivity timer, it will be restarted when response is received */
    appPeerSigCancelInactivityTimer();
}

/******************************************************************************
 * Handlers for peer signalling internal messages
 ******************************************************************************/
/*! \brief Send link key to peer earbud.
 */
static void appPeerSigHandleInternalLinkKeyRequest(PEER_SIG_INTERNAL_LINK_KEY_REQ_T *req)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOGF("appPeerSigHandleInternalLinkKeyRequest, state %u", appPeerSigGetState());

    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_CONNECTED:
        {
            uint8 message[AVRCP_PEER_CMD_ADD_LINK_KEY_SIZE];
            int index;

            /* Remember handset address */
            peer_sig->handset_addr = req->handset_addr;

            /* Build data for message, handset address and key */
            index = AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_OFFSET;
            message[index] = AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_BREDR;
            index = AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_OFFSET;
            message[index++] =  req->handset_addr.lap & 0xFF;
            message[index++] = (req->handset_addr.lap >> 8) & 0xFF;
            message[index++] = (req->handset_addr.lap >> 16) & 0xFF;
            message[index++] =  req->handset_addr.uap;
            message[index++] =  req->handset_addr.nap & 0xFF;
            message[index++] = (req->handset_addr.nap >> 8) & 0xFF;
            index = AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_TYPE_OFFSET;
            message[index] = AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_TYPE_0;
            index = AVRCP_PEER_CMD_ADD_LINK_KEY_KEY_OFFSET;
            memcpy(&message[index], req->key, req->key_len);

            /* Send the link key over AVRCP */
            appPeerSigVendorPassthroughRequest(req->client_task, AVRCP_PEER_CMD_ADD_LINK_KEY,
                                               AVRCP_PEER_CMD_ADD_LINK_KEY_SIZE, message);
        }
        break;

        default:
        {
            appPeerSigMsgLinkKeyConfirmation(req->client_task, peerSigStatusLinkKeyTxFail,
                                             &req->handset_addr);
        }
        break;
    }
}

/*! \brief Send handset pair command to peer earbud. */
static void appPeerSigHandleInternalPairHandsetRequest(PEER_SIG_INTERNAL_PAIR_HANDSET_REQ_T *req)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    DEBUG_LOGF("appPeerSigHandleInternalPairHandsetRequest, state %u", appPeerSigGetState());

    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_CONNECTED:
        {
            uint8 message[AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_SIZE];
            int index;

            /* Remember handset address */
            peer_sig->handset_addr = req->handset_addr;

            /* Build data for message and handset address */
            index = AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_ADDR_TYPE_OFFSET;
            message[index] = AVRCP_PEER_CMD_ADD_LINK_KEY_ADDR_TYPE_BREDR;
            index = AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_ADDR_OFFSET;
            message[index++] =  req->handset_addr.lap & 0xFF;
            message[index++] = (req->handset_addr.lap >> 8) & 0xFF;
            message[index++] = (req->handset_addr.lap >> 16) & 0xFF;
            message[index++] =  req->handset_addr.uap;
            message[index++] =  req->handset_addr.nap & 0xFF;
            message[index++] = (req->handset_addr.nap >> 8) & 0xFF;

            /* Send the handset address over AVRCP */
            appPeerSigVendorPassthroughRequest(req->client_task, AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS,
                                               AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS_SIZE, message);
        }
        break;

        default:
        {
            appPeerSigMsgPairHandsetConfirmation(req->client_task, peerSigStatusLinkKeyTxFail,
                                                 &req->handset_addr);
        }
        break;
    }
}

/*! \brief Send a Msg Channel transmission to the peer earbud.
 */
static void appPeerSigHandleInternalMsgChannelTxRequest(const PEER_SIG_INTERNAL_MSG_CHANNEL_TX_REQ_T* req)
{
    peerSigTaskData *peer_sig = appGetPeerSig();

    DEBUG_LOGF("appPeerSigHandleInternalMsgChannelTxRequest, state %u chan %u size %u",
                appPeerSigGetState(), req->channel, req->msg_size);

    switch (appPeerSigGetState())
    {
        case PEER_SIG_STATE_CONNECTED:
        {
            /* \todo could optimise to reduce malloc for messages small enough to put on the stack */
            uint8* message = PanicUnlessMalloc(AVRCP_PEER_CMD_MSG_CHANNEL_HEADER_SIZE + req->msg_size);

            /* remember message channel for confirmation messages */
            peer_sig->current_msg_channel = req->channel;

            appPeerSigWriteUint32(&message[AVRCP_PEER_CMD_MSG_CHANNEL_ID_OFFSET], req->channel);
            appPeerSigWriteUint16(&message[AVRCP_PEER_CMD_MSG_CHANNEL_DATA_LENGTH_OFFSET], req->msg_size);
            memcpy(&message[AVRCP_PEER_CMD_MSG_CHANNEL_DATA_OFFSET], req->msg, req->msg_size);

            appPeerSigVendorPassthroughRequest(req->client_task, AVRCP_PEER_CMD_MSG_CHANNEL_MSG,
                                               AVRCP_PEER_CMD_MSG_CHANNEL_HEADER_SIZE + req->msg_size, message);
            free(message);
        }
        break;

        default:
        {
            /* send tx confirmation fail message to task registered for the channel */
            appPeerSigMsgChannelTxConfirmation(req->client_task, peerSigStatusMsgChannelTxFail,
                                               req->channel);
        }
        break;
    }
}

/*! \brief Peer signalling task message handler.
 */
static void appPeerSigHandleMessage(Task task, MessageId id, Message message)
{
    UNUSED(task);

    switch (id)
    {
        /* AV module peer connection related messages */
        case AV_AVRCP_CONNECT_CFM:
            appPeerSigHandleAvAvrcpConnectCfm((AV_AVRCP_CONNECT_CFM_T *)message);
            break;

        case AV_AVRCP_CONNECT_IND:
            appPeerSigHandleAvAvrcpConnectInd((AV_AVRCP_CONNECT_IND_T *)message);
            break;

        case AV_AVRCP_DISCONNECT_IND:
            appPeerSigHandleAvAvrcpDisconnectInd((AV_AVRCP_DISCONNECT_IND_T *)message);
            break;

        /* AV module peer comms related messages */
        case AV_AVRCP_VENDOR_PASSTHROUGH_IND:
            appPeerSigHandleAvAvrcpVendorPassthroughInd((AV_AVRCP_VENDOR_PASSTHROUGH_IND_T*)message);
            break;

        case AV_AVRCP_VENDOR_PASSTHROUGH_CFM:
            appPeerSigHandleAvAvrcpVendorPassthroughConfirm((AV_AVRCP_VENDOR_PASSTHROUGH_CFM_T*)message);
            break;            

        /* Ignore volume messages */
        case AV_AVRCP_SET_VOLUME_IND:
        case AV_AVRCP_VOLUME_CHANGED_IND:
            break;

        /* Ignore play status messages */
        case AV_AVRCP_PLAY_STATUS_CHANGED_IND:
            break;
        
        /* Internal Peer Signalling Messages */
        case PEER_SIG_INTERNAL_STARTUP_REQ:
            appPeerSigHandleInternalStartupRequest((PEER_SIG_INTERNAL_STARTUP_REQ_T *)message);
            break;

        case PEER_SIG_INTERNAL_INACTIVITY_TIMER:
            appPeerSigInactivityTimeout();
            break;

        case PEER_SIG_INTERNAL_LINK_KEY_REQ:
            appPeerSigHandleInternalLinkKeyRequest((PEER_SIG_INTERNAL_LINK_KEY_REQ_T *)message);
            break;

        case PEER_SIG_INTERNAL_PAIR_HANDSET_REQ:
            appPeerSigHandleInternalPairHandsetRequest((PEER_SIG_INTERNAL_PAIR_HANDSET_REQ_T *)message);
            break;

        case PEER_SIG_INTERNAL_SHUTDOWN_REQ:
            appPeerSigHandleInternalShutdownReq();
            break;

        case PEER_SIG_INTERNAL_MSG_CHANNEL_TX_REQ:
            appPeerSigHandleInternalMsgChannelTxRequest((PEER_SIG_INTERNAL_MSG_CHANNEL_TX_REQ_T*)message);
            break;

        default:
            DEBUG_LOGF("appPeerSigHandleMessage. Unhandled message 0x%04x (%d)",id,id);
            break;
    }
}

/******************************************************************************
 * PUBLIC API
 ******************************************************************************/
/* Initialise the peer signalling module. 
 */
void appPeerSigInit(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    memset(peer_sig, 0, sizeof(*peer_sig));

    /* Set task's message handler */
    peer_sig->task.handler = appPeerSigHandleMessage;

    /* Set initial state and ensure lock is cleared */
    peer_sig->state = PEER_SIG_STATE_NULL;
    peer_sig->lock = 0;
    peer_sig->link_loss_occurred = FALSE;

    /* Create the list of peer signalling clients that receive
     * PEER_SIG_CONNECTION_IND messages. */
    peer_sig->peer_sig_client_tasks = appTaskListInit();

    /* Create a TaskListWithData to track client tasks
     * for specific message channels. */
    peer_sig->msg_channel_tasks = appTaskListWithDataInit();

    /* Move to 'disconnected' state */
    appPeerSigSetState(PEER_SIG_STATE_DISCONNECTED);
}

/* Send handset link key to peer headset.
 
    External API for link keys is 8 packed 16-bit words, internally and over
    AVRCP we use 8-bit words, make the conversion now.
 */
void appPeerSigLinkKeyToPeerRequest(Task task, const bdaddr *peer_addr, const bdaddr *handset_addr,
                                    const uint16 *key, uint16 key_len)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    uint16 size_key_bytes = key_len * 2;
    MAKE_PEER_SIG_MESSAGE_WITH_LEN(PEER_SIG_INTERNAL_LINK_KEY_REQ, size_key_bytes-1);

    DEBUG_LOGF("appPeerSigLinkKeyToPeerRequest, bdaddr %04x,%02x,%06lx",
               handset_addr->nap, handset_addr->uap, handset_addr->lap);

    /* Build message to trigger TX of link key to peer, will wait for peer AVRCP
     * connection if none exists yet, or another operation if one already in
     * progress */
    message->client_task = task;
    message->handset_addr = *handset_addr;
    message->key_len = size_key_bytes;
    memcpy(message->key, key, size_key_bytes);
    MessageSendConditionally(&peer_sig->task, PEER_SIG_INTERNAL_LINK_KEY_REQ,
                             message, appPeerSigStartup(peer_addr));
}

/* Inform peer earbud of address of handset with which it should pair.
*/
void appPeerSigTxPairHandsetRequest(Task task, const bdaddr *peer_addr, const bdaddr *handset_addr)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    MAKE_MESSAGE(PEER_SIG_INTERNAL_PAIR_HANDSET_REQ);

    DEBUG_LOGF("appPeerSigTxPairHandsetRequest, bdaddr %04x,%02x,%06lx",
               handset_addr->nap, handset_addr->uap, handset_addr->lap);

    message->client_task = task;
    message->handset_addr = *handset_addr;
    MessageSendConditionally(&peer_sig->task, PEER_SIG_INTERNAL_PAIR_HANDSET_REQ,
                             message, appPeerSigStartup(peer_addr));
}

/*! \brief Request a transmission on a message channel.
 */
void appPeerSigMsgChannelTxRequest(Task task,
                                   const bdaddr* peer_addr,
                                   peerSigMsgChannel channel,
                                   const uint8* msg, uint16 msg_size)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    MAKE_PEER_SIG_MESSAGE_WITH_LEN(PEER_SIG_INTERNAL_MSG_CHANNEL_TX_REQ, msg_size-1);

    DEBUG_LOG("appPeerSigMsgChannelTxRequest");
    
    message->client_task = task;
    message->channel = channel;
    message->msg_size = msg_size;
    memcpy(message->msg, msg, msg_size);

    /* Send to task, potentially blocked on bringing up AVRCP */
    MessageSendConditionally(&peer_sig->task, PEER_SIG_INTERNAL_MSG_CHANNEL_TX_REQ,
                             message, appPeerSigStartup(peer_addr));
}

/* Register task with peer signalling for Link Key TX/RX operations.
 */
void appPeerSigLinkKeyTaskRegister(Task client_task)
{
    peerSigTaskData *peer_sig = appGetPeerSig();

    DEBUG_LOG("appPeerSigLinkKeyTaskRegister");
    
    /* remember client task for when AVRCP connects */
    peer_sig->rx_link_key_task = client_task;

    /* tell AV we want notification when an AVRCP connection occurs */
    appAvAvrcpClientRegister(&peer_sig->task, 0);
}

/* Unregister task with peer signalling for Link Key TX/RX operations.
 */
void appPeerSigLinkKeyTaskUnregister(void)
{
    peerSigTaskData* peer_sig = appGetPeerSig();

    DEBUG_LOG("appPeerSigLinkKeyTaskUnregister");
    
    /* remember client task for when AVRCP connects */
    peer_sig->rx_link_key_task = NULL;
}

/* Try and connect peer signalling channel with specified peer earbud.
 */
void appPeerSigConnect(bdaddr* peer_addr)
{
    DEBUG_LOGF("appPeerSigConnect %lx %x %x", peer_addr->lap, peer_addr->uap, peer_addr->nap);

    /* get AVRCP channel to peer if required */
    appPeerSigStartup(peer_addr);
}

/* Register to receive peer signalling notifications. */
void appPeerSigClientRegister(Task client_task)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    appTaskListAddTask(peer_sig->peer_sig_client_tasks, client_task);
}

/* Unregister to stop receiving peer signalling notifications. */
void appPeerSigClientUnregister(Task client_task)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    appTaskListRemoveTask(peer_sig->peer_sig_client_tasks, client_task);
}

/* Register task to receive AVRCP_PEER_CMD_PAIR_HANDSET_ADDRESS. */
void appPeerSigPairHandsetTaskRegister(Task pair_handset_task)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    peer_sig->rx_pair_handset_task = pair_handset_task;
}

/*! \brief Register to receive PEER_SIG_MSG_CHANNEL_RX_IND messages for a channel mask.
    \param task [in] Task to receive incoming messages on the channels in channel_mask.
    \param channel_mask [in] Mask of channels IDs registered to messages.
 */
void appPeerSigMsgChannelTaskRegister(Task task, peerSigMsgChannel channel_mask)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    TaskListData data = {0};

    if (appTaskListGetDataForTask(peer_sig->msg_channel_tasks, task, &data))
    {
        data.u32 |= channel_mask;
        appTaskListSetDataForTask(peer_sig->msg_channel_tasks, task, &data);
    }
    else
    {
        data.u32 |= channel_mask;
        appTaskListAddTaskWithData(peer_sig->msg_channel_tasks, task, &data);
    }
}

/*! \brief Stop receiving PEER_SIG_MSG_CHANNEL_RX_IND messages on a channel mask.
    \param task [in] Task to stop receiving incoming messages for the channels in channel_mask.
    \param channel_mask [in] Mask of channels IDs to unregister for messages.
*/
void appPeerSigMsgChannelTaskUnregister(Task task, peerSigMsgChannel channel_mask)
{
    peerSigTaskData* peer_sig = appGetPeerSig();
    TaskListData data = {0};

    if (appTaskListGetDataForTask(peer_sig->msg_channel_tasks, task, &data))
    {
        data.u32 &= ~channel_mask;
        if (data.u32)
        {
            appTaskListSetDataForTask(peer_sig->msg_channel_tasks, task, &data);
        }
        else
        {
            appTaskListRemoveTask(peer_sig->msg_channel_tasks, task);
        }
    }
}

/* Force peer signalling to disconnect AVRCP if it is up. */
void appPeerSigForceDisconnect(void)
{
    peerSigTaskData *peer_sig = appGetPeerSig();
    MessageSendConditionally(&peer_sig->task, PEER_SIG_INTERNAL_SHUTDOWN_REQ, NULL, &peer_sig->lock);
}

/******************************************************************************
 * TEST FUNCTIONS
 ******************************************************************************/
void appPeerSigForceLinkToPeer(void)
{
    appDeviceAttributes attr;
    bdaddr peer_addr;

    if (appDeviceGetPeerAttributes(&peer_addr, &attr))
    {
        appPeerSigConnect(&peer_addr);
    }
    else
        DEBUG_LOG("No peer earbud paired");
}
